package com.hanwei.uap.common.util.bytes;

import com.hanwei.uap.common.data.check.CRC16;
import com.hanwei.uap.common.util.ByteUtil;

import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;


/**
 * 汉威小型空气站工具类
 *
 * @author zhaoam
 * @create 2017-08-02 10:01
 **/
public class SstnUtil {
    /**
     * 将字节数组中的指定字节进行转义 0x3A->0x7E0x3B 0x0D->0x7E0x0E 0x7E->0x7E0x7F
     *
     * @param bytes
     * @return
     */
    public static byte[] transfer(byte[] bytes) {
        byte[] data = null;
        Map<Integer, Integer> indexMap = new HashMap<>();
        for (int i = 1; i <= bytes.length - 2; i++) {
            if (bytes[i] == 0x3A || bytes[i] == 0x0D || bytes[i] == 0x7E)
                indexMap.put(i, i);
        }
        if (indexMap.size() != 0) {
            data = new byte[bytes.length + indexMap.size()];
            for (int i = 0, j = 0; i < bytes.length; i++, j++) {
                if (indexMap.get(i) == null) {
                    data[j] = bytes[i];
                } else {
                    if (bytes[i] == 0x3A) {
                        data[j] = 0x7E;
                        data[++j] = 0x3B;
                    } else if (bytes[i] == 0x0D) {
                        data[j] = 0x7E;
                        data[++j] = 0x0E;
                    } else if (bytes[i] == 0x7E) {
                        data[j] = 0x7E;
                        data[++j] = 0x7F;
                    }
                }
            }
        }
        if (data != null)
            return data;
        else
            return bytes;
    }

    /**
     * 将字节数组中转义过的字节恢复成原始的字节 0x7E0x3B->0x3A 0x7E0x0E->0x0D 0x7E0x7F->0x7E
     *
     * @param bytes
     * @return
     */
    public static byte[] untransfer(byte[] bytes) {
        byte[] data = null;
        Map<Integer, Integer> indexMap = new HashMap<Integer, Integer>();
        for (int i = 1; i < bytes.length - 1; i++) {
            if (bytes[i] == 0x7E)
                indexMap.put(i, i);
        }
        if (indexMap.size() != 0) {
            data = new byte[bytes.length - indexMap.size()];
            for (int i = 0, j = 0; i < bytes.length; i++, j++) {
                if (indexMap.get(i) == null) {
                    data[j] = bytes[i];
                } else {
                    if (bytes[i + 1] == 0x3B) {
                        data[j] = 0x3A;
                    } else if (bytes[i + 1] == 0x0E) {
                        data[j] = 0x0D;
                    } else if (bytes[i + 1] == 0x7F) {
                        data[j] = 0x7E;
                    }
                    i++;
                }
            }
        }
        if (data != null)
            return data;
        else
            return bytes;
    }

    /**
     * 根据完整的数据帧生成应答帧
     *
     * @param data
     * @return
     */
    public static byte[] getReplyData(byte[] data) {
        byte[] reply = new byte[16];
        reply[0] = 0x3A;
        reply[1] = data[1];
        reply[2] = data[2];
        reply[3] = data[3];
        reply[4] = data[4];
        reply[5] = data[5];
        reply[6] = data[6];
        reply[7] = data[7];
        reply[8] = data[8];
        reply[9] = data[9];
        reply[10] = data[10];
        reply[11] = data[11];
        reply[12] = data[12];
        byte[] bytes = new byte[12];
        System.arraycopy(data, 1, bytes, 0, 12);
        int crc = CRC16.getCrc16(bytes);

        reply[13] = (byte) (crc & 0xff);
        reply[14] = (byte) ((crc >> 8) & 0xff);
        reply[reply.length - 1] = 0x0D;
        return transfer(reply);
    }

    /**
     * 根据完整的数据帧生成校准时间帧
     *
     * @param data
     * @return
     */
    public static byte[] getCorrectionTimeData(byte[] data) {
        byte[] v = new byte[24];
        v[0] = 0x3A;
        v[1] = data[1];
        v[2] = data[2];
        v[3] = data[3];
        v[4] = data[4];
        v[5] = data[5];
        v[6] = data[6];
        v[7] = data[7];
        v[8] = data[8];
        v[9] = data[9];
        v[10] = data[10];
        v[11] = 0x11;
        v[12] = 0x01;
        v[13] = 0x30;
        v[14] = 0x00;
        Calendar calendar = Calendar.getInstance();
        int year = calendar.get(Calendar.YEAR);
        v[15] = (byte) (calendar.get(Calendar.SECOND) & 0xff);
        v[16] = (byte) (calendar.get(Calendar.MINUTE) & 0xff);
        v[17] = (byte) (calendar.get(Calendar.HOUR_OF_DAY) & 0xff);
        v[18] = (byte) (calendar.get(Calendar.DAY_OF_MONTH) & 0xff);
        v[19] = (byte) ((calendar.get(Calendar.MONTH) + 1) & 0xff);
        v[20] = (byte) ((year % 100) & 0xff);
        byte[] bytes = new byte[20];
        System.arraycopy(v, 1, bytes, 0, 20);
        int crc = CRC16.getCrc16(bytes);
        v[21] = (byte) (crc & 0xff);
        v[22] = (byte) ((crc >> 8) & 0xff);
        v[v.length - 1] = 0x0D;
        return transfer(v);
    }

    public static byte[] getNetParaReadBytes(String deviceId, int deviceType) {
        byte[] v = new byte[16];
        v[0] = 0x3A;
        byte[] d = ByteUtil.hex2byte(deviceId);
        v[1] = d[4];
        v[2] = d[3];
        v[3] = d[2];
        v[4] = d[1];
        v[5] = d[0];
        v[9] = (byte) (deviceType & 0xff);
        v[10] = (byte) ((deviceType >> 8) & 0xff);
        v[11] = 0x20;
        v[12] = 0x00;
        CRC16.putCrc16(v);
        v[v.length - 1] = 0x0D;
        return transfer(v);
    }

    public static byte[] getNetParaReadBytes() {
        byte[] v = new byte[16];
        v[0] = 0x3A;
        v[11] = 0x20;
        v[12] = 0x00;
        v[v.length - 1] = 0x0D;
        return v;
    }

    /**
     * @param deviceId   设备编号
     * @param deviceType 设备类型
     * @param net        网络模式 0:TCP,1:UDP
     * @param host       服务器地址或域名
     * @param port       服务器端口号
     * @param apnname    APN接入点名称
     * @param apnuser    APN用户名
     * @param apnpass    APN密码
     * @return
     */
    public static byte[] getNetParaWriteBytes(String deviceId, int deviceType, int net, String host, String port,
                                              String apnname, String apnuser, String apnpass) {
        byte[] v = new byte[96];
        v[0] = 0x3A;
        byte[] d = ByteUtil.hex2byte(deviceId);
        v[1] = d[4];
        v[2] = d[3];
        v[3] = d[2];
        v[4] = d[1];
        v[5] = d[0];
        v[9] = (byte) (deviceType & 0xff);
        v[10] = (byte) ((deviceType >> 8) & 0xff);
        v[11] = 0x21;
        v[12] = 0x00;
        v[13] = (byte) (net & 0xff);
        v[14] = (byte) ((net >> 8) & 0xff);
        byte[] hostbytes = host.getBytes();
        System.arraycopy(hostbytes, 0, v, 15, hostbytes.length);
        byte[] portbytes = port.getBytes();
        System.arraycopy(portbytes, 0, v, 47, portbytes.length);
        byte[] namebytes = apnname.getBytes();
        System.arraycopy(namebytes, 0, v, 53, namebytes.length);
        byte[] userbytes = apnuser.getBytes();
        System.arraycopy(userbytes, 0, v, 61, userbytes.length);
        byte[] passbytes = apnpass.getBytes();
        System.arraycopy(passbytes, 0, v, 81, passbytes.length);
        CRC16.putCrc16(v);
        v[v.length - 1] = 0x0D;
        return transfer(v);
    }

    public static byte[] getNetParaWriteBytes(int net, String host, String port, String apnname, String apnuser,
                                              String apnpass) {
        byte[] v = new byte[96];
        v[0] = 0x3A;
        v[11] = 0x21;
        v[12] = 0x00;
        v[13] = (byte) (net & 0xff);
        v[14] = (byte) ((net >> 8) & 0xff);
        byte[] hostbytes = host.getBytes();
        System.arraycopy(hostbytes, 0, v, 15, hostbytes.length);
        byte[] portbytes = port.getBytes();
        System.arraycopy(portbytes, 0, v, 47, portbytes.length);
        byte[] namebytes = apnname.getBytes();
        System.arraycopy(namebytes, 0, v, 53, namebytes.length);
        byte[] userbytes = apnuser.getBytes();
        System.arraycopy(userbytes, 0, v, 61, userbytes.length);
        byte[] passbytes = apnpass.getBytes();
        System.arraycopy(passbytes, 0, v, 81, passbytes.length);
        v[v.length - 1] = 0x0D;
        return transfer(v);
    }

    /**
     * 获取当前时间的hex字符串：秒分时天月年
     *
     * @return
     */
    public static String getTimeHex() {
        byte[] v = new byte[6];
        Calendar calendar = Calendar.getInstance();
        int year = calendar.get(Calendar.YEAR);
        v[0] = (byte) (calendar.get(Calendar.SECOND) & 0xff);
        v[1] = (byte) (calendar.get(Calendar.MINUTE) & 0xff);
        v[2] = (byte) (calendar.get(Calendar.HOUR_OF_DAY) & 0xff);
        v[3] = (byte) (calendar.get(Calendar.DAY_OF_MONTH) & 0xff);
        v[4] = (byte) ((calendar.get(Calendar.MONTH) + 1) & 0xff);
        v[5] = (byte) ((year % 100) & 0xff);
        return ByteUtil.byte2hex(v);
    }

    /**
     * 获取对设备进行时间同步的字节数组指令
     *
     * @param deviceId   设备编号
     * @param deviceType 设备类型
     * @return
     */
    public static byte[] getSynTimeBytes(String deviceId, int deviceType) {
        byte[] v = new byte[24];
        v[0] = 0x3A;
        byte[] d = ByteUtil.hex2byte(deviceId);
        v[1] = d[4];
        v[2] = d[3];
        v[3] = d[2];
        v[4] = d[1];
        v[5] = d[0];
        v[9] = (byte) (deviceType & 0xff);
        v[10] = (byte) ((deviceType >> 8) & 0xff);
        v[11] = 0x11;
        v[12] = 0x01;
        v[13] = 0x30;
        v[14] = 0x00;
        Calendar calendar = Calendar.getInstance();
        int year = calendar.get(Calendar.YEAR);
        v[15] = (byte) (calendar.get(Calendar.SECOND) & 0xff);
        v[16] = (byte) (calendar.get(Calendar.MINUTE) & 0xff);
        v[17] = (byte) (calendar.get(Calendar.HOUR_OF_DAY) & 0xff);
        v[18] = (byte) (calendar.get(Calendar.DAY_OF_MONTH) & 0xff);
        v[19] = (byte) ((calendar.get(Calendar.MONTH) + 1) & 0xff);
        v[20] = (byte) ((year % 100) & 0xff);
        CRC16.putCrc16(v);
        v[v.length - 1] = 0x0D;
        return transfer(v);
    }

    public static String getSynTimeHex(String deviceId, int deviceType) {
        StringBuffer cmd = new StringBuffer();
        cmd.append("3A");
        cmd.append(deviceId.substring(8, 10));
        cmd.append(deviceId.substring(6, 8));
        cmd.append(deviceId.substring(4, 6));
        cmd.append(deviceId.substring(2, 4));
        cmd.append(deviceId.substring(0, 2));
        cmd.append("000000");
        cmd.append(ByteUtil.int2hex(deviceType, 2, true));// 设备类型，协议号
        cmd.append("1101");// 通道号
        cmd.append("3000");// 命令号
        cmd.append("${curtime}");
        cmd.append("0000");
        cmd.append("0D");
        return cmd.toString();
    }

    public static byte[] getRebootNowBytes(String deviceId, int deviceType) {
        byte[] data = getRebootNowBytes();
        return putCrc16AndTransfer(deviceId, deviceType, data);
    }

    public static byte[] getRebootNowBytes() {
        byte[] v = new byte[18];
        v[0] = 0x3A;
        v[11] = 0x11;
        v[12] = 0x01;
        v[13] = 0x01;
        v[14] = 0x00;
        v[v.length - 1] = 0x0D;
        return v;
    }

    public static byte[] getClearStoreBytes(String deviceId, int deviceType) {
        byte[] data = getClearStoreBytes();
        return putCrc16AndTransfer(deviceId, deviceType, data);
    }

    public static byte[] getClearStoreBytes() {
        byte[] v = new byte[18];
        v[0] = 0x3A;
        v[11] = 0x11;
        v[12] = 0x01;
        v[13] = 0x02;
        v[14] = 0x00;
        v[v.length - 1] = 0x0D;
        return v;
    }

    public static byte[] getDisconnectServerBytes(String deviceId, int deviceType) {
        byte[] data = getDisconnectServerBytes();
        return putCrc16AndTransfer(deviceId, deviceType, data);
    }

    public static byte[] getDisconnectServerBytes() {
        byte[] v = new byte[18];
        v[0] = 0x3A;
        v[11] = 0x11;
        v[12] = 0x01;
        v[13] = 0x20;
        v[14] = 0x00;
        v[v.length - 1] = 0x0D;
        return v;
    }

    public static byte[] getUploadDataNowBytes(String deviceId, int deviceType) {
        byte[] data = getUploadDataNowBytes();
        return putCrc16AndTransfer(deviceId, deviceType, data);
    }

    public static byte[] getUploadDataNowBytes() {
        byte[] v = new byte[18];
        v[0] = 0x3A;
        v[11] = 0x11;
        v[12] = 0x01;
        v[13] = 0x21;
        v[14] = 0x00;
        v[v.length - 1] = 0x0D;
        return v;
    }

    public static byte[] getCollectSensorNowBytes(String deviceId, int deviceType) {
        byte[] data = getCollectSensorNowBytes();
        return putCrc16AndTransfer(deviceId, deviceType, data);
    }

    public static byte[] getCollectSensorNowBytes() {
        byte[] v = new byte[18];
        v[0] = 0x3A;
        v[11] = 0x11;
        v[12] = 0x01;
        v[13] = 0x22;
        v[14] = 0x00;
        v[v.length - 1] = 0x0D;
        return v;
    }

    public static byte[] getUploadControlReadBytes(String deviceId, int deviceType) {
        byte[] data = getUploadControlReadBytes();
        return putCrc16AndTransfer(deviceId, deviceType, data);
    }

    public static byte[] getUploadControlReadBytes() {
        byte[] v = new byte[16];
        v[0] = 0x3A;
        v[11] = 0x10;
        v[12] = 0x00;
        CRC16.putCrc16(v);
        v[v.length - 1] = 0x0D;
        return v;
    }

    public static byte[] getUploadControlWriteBytes(String deviceId, int deviceType, int interval, int delayTime,
                                                    int uploadInterface, int cmdTimeout, int retryTimes) {
        byte[] data = getUploadControlWriteBytes(interval, delayTime, uploadInterface, cmdTimeout, retryTimes);
        return putCrc16AndTransfer(deviceId, deviceType, data);
    }

    public static byte[] getUploadControlWriteBytes(int interval, int delayTime, int uploadInterface, int cmdTimeout,
                                                    int retryTimes) {
        byte[] v = new byte[26];
        v[0] = 0x3A;
        v[11] = 0x11;
        v[12] = 0x00;
        v[13] = (byte) (interval & 0xff);
        v[14] = (byte) ((interval >> 8) & 0xff);
        v[15] = (byte) (delayTime & 0xff);
        v[16] = (byte) ((delayTime >> 8) & 0xff);
        v[17] = (byte) (uploadInterface & 0xff);
        v[18] = (byte) ((uploadInterface >> 8) & 0xff);
        v[19] = (byte) (cmdTimeout & 0xff);
        v[20] = (byte) ((cmdTimeout >> 8) & 0xff);
        v[21] = (byte) (retryTimes & 0xff);
        v[22] = (byte) ((retryTimes >> 8) & 0xff);
        v[v.length - 1] = 0x0D;
        return v;
    }

    public static byte[] getSensorCollectReadBytes(String deviceId, int deviceType) {
        byte[] data = getSensorCollectReadBytes();
        return putCrc16AndTransfer(deviceId, deviceType, data);
    }

    public static byte[] getSensorCollectReadBytes() {
        byte[] v = new byte[16];
        v[0] = 0x3A;
        v[11] = 0x40;
        v[12] = 0x00;
        v[v.length - 1] = 0x0D;
        return v;
    }

    public static byte[] getSensorCollectWriteBytes(String deviceId, int deviceType, int interval,
                                                    Map<String, Integer> sensorIdMap) {
        byte[] data = getSensorCollectWriteBytes(interval, sensorIdMap);
        return putCrc16AndTransfer(deviceId, deviceType, data);
    }

    public static byte[] getSensorCollectWriteBytes(int interval, Map<String, Integer> sensorIdMap) {
        byte[] v = new byte[34];
        v[0] = 0x3A;
        v[11] = 0x41;
        v[12] = 0x00;
        v[13] = (byte) (interval & 0xff);
        v[14] = (byte) ((interval >> 8) & 0xff);
        for (int i = 0; i < 16; i++) {
            Integer sensorId = sensorIdMap.get("sensor" + i);
            if (null != sensorId) {
                int value = sensorId;
                v[15 + i] = (byte) value;
            }
        }
        v[v.length - 1] = 0x0D;
        return v;
    }

    public static byte[] getSensorSetReadBytes(String deviceId, int deviceType) {
        byte[] data = getSensorSetReadBytes();
        return putCrc16AndTransfer(deviceId, deviceType, data);
    }

    public static byte[] getSensorSetReadBytes() {
        byte[] v = new byte[16];
        v[0] = 0x3A;
        v[11] = 0x50;
        v[12] = 0x00;
        v[v.length - 1] = 0x0D;
        return v;
    }

    public static byte[] getSensorSetWriteBytes(String deviceId, int deviceType, Map<String, String> sensorSetMap) {
        byte[] data = getSensorSetWriteBytes(sensorSetMap);
        return putCrc16AndTransfer(deviceId, deviceType, data);
    }

    public static byte[] getSensorSetWriteBytes(Map<String, String> sensorSetMap) {
        byte[] v = new byte[144];
        v[0] = 0x3A;
        v[11] = 0x51;
        v[12] = 0x00;
        for (int i = 0; i < 16; i++) {
            String setHex = sensorSetMap.get("sensor" + i);
            if (null != setHex) {
                byte[] bytes = ByteUtil.hex2byte(setHex);
                System.arraycopy(bytes, 0, v, 13 + i * 8, 8);
            }
        }
        v[v.length - 1] = 0x0D;
        return v;
    }

    public static byte[] getScreenControlReadBytes(String deviceId, int deviceType) {
        byte[] data = getScreenControlReadBytes();
        return putCrc16AndTransfer(deviceId, deviceType, data);
    }

    public static byte[] getScreenControlReadBytes() {
        byte[] v = new byte[16];
        v[0] = 0x3A;
        v[11] = 0x60;
        v[12] = 0x00;
        v[v.length - 1] = 0x0D;
        return v;
    }

    public static byte[] getScreenControlWriteBytes(String deviceId, int deviceType, int refreshInterval,
                                                    int screenType, Map<String, String> labelSetMap) {
        byte[] data = getScreenControlWriteBytes(refreshInterval, screenType, labelSetMap);
        return putCrc16AndTransfer(deviceId, deviceType, data);
    }

    public static byte[] getScreenControlWriteBytes(int refreshInterval, int screenType,
                                                    Map<String, String> labelSetMap) {
        byte[] v = new byte[68];
        v[0] = 0x3A;
        v[11] = 0x61;
        v[12] = 0x00;
        v[13] = (byte) (refreshInterval & 0xff);
        v[14] = (byte) ((refreshInterval >> 8) & 0xff);
        v[15] = (byte) (screenType & 0xff);
        v[16] = (byte) ((screenType >> 8) & 0xff);
        for (int i = 0; i < 24; i++) {
            String setHex = labelSetMap.get("label" + i);
            if (null != setHex) {
                byte[] bytes = ByteUtil.hex2byte(setHex);
                v[17 + i * 2] = bytes[0];
                v[18 + i * 2] = bytes[1];
            }
        }
        v[v.length - 1] = 0x0D;
        return v;
    }

    public static byte[] getReadStatusBytes(String deviceId, int deviceType) {
        byte[] data = getReadStatusBytes();
        return putCrc16AndTransfer(deviceId, deviceType, data);
    }

    public static byte[] getReadStatusBytes() {
        byte[] v = new byte[16];
        v[0] = 0x3A;
        v[11] = 0x00;
        v[12] = 0x01;
        v[v.length - 1] = 0x0D;
        return v;
    }

    public static byte[] putCrc16AndTransfer(String deviceId, int deviceType, byte[] data) {
        byte[] d = ByteUtil.hex2byte(deviceId);
        data[1] = d[4];
        data[2] = d[3];
        data[3] = d[2];
        data[4] = d[1];
        data[5] = d[0];
        data[9] = (byte) (deviceType & 0xff);
        data[10] = (byte) ((deviceType >> 8) & 0xff);
        CRC16.putCrc16(data);
        return transfer(data);
    }
}
